package geppetto.reordering;

import java.util.LinkedList;

import geppetto.cat.alignments.Alignment;
import geppetto.cat.alignments.AlignmentsCache;
import geppetto.cat.alignments.AlignmentsSet;
import geppetto.phraseHMM.phraseExtraction.extractedphrase.ExtractedPhrasePair;
import gnu.trove.TIntDoubleHashMap;

public class PhraseMSDExtendedReorderingCalculator extends MSDReorderingCalculator{

	protected REO_MODEL_TYPE modelType;
	AlignmentsSet set;
	AlignmentsCache _cache;
	protected TIntDoubleHashMap prevSmoothingFactors = new TIntDoubleHashMap();
	protected TIntDoubleHashMap nextSmoothingFactors = new TIntDoubleHashMap();
			
	public PhraseMSDExtendedReorderingCalculator(REO_MODEL_TYPE modelType,AlignmentsSet set) {
		this(DEFAULTSMOOTHING,modelType, set);
	}
	
	public PhraseMSDExtendedReorderingCalculator(double smoothing, REO_MODEL_TYPE modelType,AlignmentsSet set) {
		super(smoothing, modelType, set);
		this.modelType = modelType;
		this.set = set;
		setSmoothing(smoothing);
		_cache = new AlignmentsCache(set);
	}

	public PhraseMSDExtendedReorderingCalculator(AlignmentsSet set) {
		this(REO_MODEL_TYPE.REO_MSD, set);
	}
	
	@Override
	public ReorderingInfo getReorderingInfo(ExtractedPhrasePair epp){
		 int sentenceNumber = epp.getSentenceNumber();
		 int startS = epp.getSourceStartPos();
		 int endS = epp.getSourceEndPos();
		 int startT = epp.getForeignStartPos();
		 int endT = epp.getForeignEndPos(); 
		 
		Alignment alignment = set.get(sentenceNumber);
		double connectedLeftTopP = getAlignmentProb(alignment, sentenceNumber, startS, startT, -1 , -1);
		double connectedRightTopP = getAlignmentProb(alignment, sentenceNumber,endS, startT, 1, -1 );
		double connectedLeftTopN = getAlignmentProb(alignment, sentenceNumber,endS, endT, 1, 1 );
		double connectedRightTopN = getAlignmentProb(alignment, sentenceNumber,startS, endT, -1, 1); 
		
		double probLeftP = connectedLeftTopP * (1 - connectedRightTopP);
		double probRightP = connectedRightTopP * (1 - connectedLeftTopP);
		double probUnkP = connectedLeftTopP * connectedRightTopP + (1 - connectedLeftTopP) * (1 - connectedRightTopP);
		double probLeftN = connectedLeftTopN * (1 - connectedRightTopN);
		double probRightN = connectedRightTopN * (1 - connectedLeftTopN);
		double probUnkN = connectedLeftTopN * connectedRightTopN + (1 - connectedLeftTopN) * (1 - connectedRightTopN);

		ReorderingInfo info = new ReorderingInfo();
		info.addPrevWordOrientation(convertREO_POS(REO_POS.LEFT), probLeftP);
		info.addPrevWordOrientation(convertREO_POS(REO_POS.RIGHT), probRightP);
		info.addPrevWordOrientation(convertREO_POS(REO_POS.UNKNOWN), probUnkP);
		info.addNextWordOrientation(convertREO_POS(REO_POS.LEFT), probLeftN);
		info.addNextWordOrientation(convertREO_POS(REO_POS.RIGHT), probRightN);
		info.addNextWordOrientation(convertREO_POS(REO_POS.UNKNOWN), probUnkN);
		
		return info;
	}
	
	public int convertREO_POS(REO_POS orient){
		switch(orient){
		 	case LEFT: return MONO_CODE;
		 	case RIGHT: return SWAP_CODE;
		 	case DRIGHT: return DRIGHT_CODE;
		 	case DLEFT: return DLEFT_CODE;
		 	case UNKNOWN:
		 	switch(modelType){
		 	case REO_MONO: return NOMONO_CODE;
		 	case REO_MSD: return OTHER_CODE;
		 	case REO_MSLR: return DRIGHT_CODE;
		 	}
		 	break;
		 	} 
		return OTHER_CODE; // this should not happen
	}
	
	private double getAlignmentProb(Alignment alignment, int sentence , int posS, int posT, int descS, int descT){
		if((-1 == posT + descT && -1 == posS + descS) || (alignment._foreignLen == posT + descT && alignment._sourceLen == posS + descS)){
			return 1;
		}
		return getPhraseAlignedProb(alignment, sentence, posS, posT, descS, descT);
	}

	private double getPhraseAlignedProb(Alignment alignment, int sentence , int posS, int posT, int descS, int descT){
		int reqS = posS + descS;
		int reqT = posT + descT;
		if(outOfBounds(alignment, reqS, reqT)){
			return 0;
		}
		int sourcePos = -1;
		int targetPos = -1;
		if(descS > 0 && descT > 0){
			sourcePos = 1;
			targetPos = 3;			
		}
		if(descS < 0 && descT > 0){
			sourcePos = 2;
			targetPos = 3;						
		}
		if(descS > 0 && descT < 0){
			sourcePos = 1;
			targetPos = 4;			
		}
		if(descS < 0 && descT < 0){
			sourcePos = 2;
			targetPos = 4;			
		}
		Integer[] minimalAlignmentPositions = null;
		for(Integer[] positions : _cache.getPhrasesWithPoint(sentence, reqS, reqT)){
			 if(reqS == positions[sourcePos] && reqT == positions[targetPos]){
				 if(minimalAlignmentPositions == null || minimalAlignmentPositions[0] >= positions[0]){
					 minimalAlignmentPositions = positions;
				 }
			 }
		}
		if(minimalAlignmentPositions == null){
			return 0;
		}
		int minS = minimalAlignmentPositions[1];
		int maxS = minimalAlignmentPositions[2];
		int minT = minimalAlignmentPositions[3];
		int maxT = minimalAlignmentPositions[4];
		LinkedList<Integer[]> phrases = new LinkedList<Integer[]>();
		for(int i = minS; i <= maxS; i++){
			for(int j = minT; j <= maxT; j++){
				for(Integer[] extracted : _cache.getPhrasesWithPoint(sentence, i, j)){
					if(!phrases.contains(extracted) && isWithin(extracted[1], extracted[2], extracted[3], extracted[4], minimalAlignmentPositions[1],minimalAlignmentPositions[2],minimalAlignmentPositions[3],minimalAlignmentPositions[4])){
						phrases.add(extracted);
					}
				}
			}
		}
		double result = 0;
		for(Integer[] extracted : phrases){
			if(reqS == extracted[sourcePos] && reqT == extracted[targetPos]){
				result++;
			}
		}
		return result / phrases.size();
	}
	
	private boolean outOfBounds(Alignment alignment, int posS, int posT){
		return posS < 0 || posT < 0 || posS >= alignment.getSourceLenght() || posT >= alignment.getForeignLenght();		
	}
	
	//tests if 1 is within 2
	private boolean isWithin(int minS, int maxS, int minT, int maxT, int minS2, int maxS2, int minT2, int maxT2){
		return minS >= minS2 && maxS2 >= maxS && minT >= minT2 && maxT2 >= maxT; 
	}
}
